/******************************************************
 * @file file.c
 * @author Destiny 
 * @brief 文件操作相关函数
 * 
 * @version 0.1
 * @date 2024-07-18
 *****************************************************/
#include "file.h"
#include "picfmt_manager.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>



/*
 * @description		    	: 将指定文件映射到进程地址空间
 * @param - ptFileMap	 	: 文件描述结构体
 * @return 			    	: 0 成功;其他 失败
 */
int MapFile(PT_FileMap ptFileMap)
{
	int iFd;
    FILE *tFp;
	struct stat tStat;
	/* 以流、读写方式打开文件 */
	tFp = fopen(ptFileMap->strFileName, "r+");
	if (tFp == NULL)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't open %s\n", ptFileMap->strFileName);
		return -1;
	}
	ptFileMap->tFp = tFp;
	/*文件指针转换为文件描述符*/
    iFd = fileno(tFp);
	/*读取文件参数*/
	fstat(iFd, &tStat);
	ptFileMap->iFileSize = tStat.st_size;
	ptFileMap->pucFileMapMem = (uint8_t *)mmap(NULL , tStat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, iFd, 0);
	if (ptFileMap->pucFileMapMem == (uint8_t *)-1)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"mmap error!\n");
		return -1;
	}
	return 0;
}
/*
 * @description		    	: 取消文件地址映射
 * @param - ptFileMap	 	: 文件描述结构体
 * @return 			    	: 0 成功;其他 失败
 */
void UnMapFile(PT_FileMap ptFileMap)
{
	munmap(ptFileMap->pucFileMapMem, ptFileMap->iFileSize);
	fclose(ptFileMap->tFp);
}

/*
 * @description		    	: 判断文件是否为目录
 * @param - strFilePath	 	: 文件路径
 * @param - strFileName	 	: 文件名称
 * @return 			    	: 1 是;其他 不是
 */
static int isDir(char *strFilePath, char *strFileName)
{
    char strTmp[256];
    struct stat tStat;
	/* 得到绝对路径 */
    snprintf(strTmp, 256, "%s/%s", strFilePath, strFileName);
    strTmp[255] = '\0';
    /**/
    if ((stat(strTmp, &tStat) == 0) && S_ISDIR(tStat.st_mode))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
/*
 * @description		    	: 判断一个文件是否常规的文件,设备节点/链接文件/FIFO文件等是特殊文件
 * @param - strFilePath	 	: 文件路径
 * @param - strFileName	 	: 文件名称
 * @return 			    	: 1 是;其他 不是
 */
static int isRegFile(char *strFilePath, char *strFileName)
{
    char strTmp[256];
    struct stat tStat;

    snprintf(strTmp, 256, "%s/%s", strFilePath, strFileName);
    strTmp[255] = '\0';

    if ((stat(strTmp, &tStat) == 0) && S_ISREG(tStat.st_mode))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/*
 * @description		    	: 把某目录下所含的顶层子目录、顶层文件都记录下来,并且按名字排序
 * @param - strDirName	 	: 目录
 * @param - pptDirContents	: 目录下文件，已链表形式存储
 * @param - piNumber		: 目录下文件数
 * @return 			    	: 1 是;其他 不是
 */
int GetDirContents(char *strDirName, PT_DirContent **pptDirContents, int *piNumber)	
{
    PT_DirContent *aptDirContents;
	struct dirent **aptNameList;
	int iNumber;
	int i;
	int j;
    char *pcRet;
/*int scandir(const char* dir, struct dirent*** namelist, int (*filter)(const struct dirent*), int (*compar)(const struct dirent**, const struct dirent**));
参数解释：

dir：要列出文件和子目录的目录路径。
namelist：指向指针数组的指针，它将用于存储扫描结果。每个指针指向一个 struct dirent 结构体，它包含文件或子目录的相关信息。
filter：函数指针，用于筛选要返回的文件和子目录。可选参数，可以传递 NULL 来返回所有文件和子目录。
compar：函数指针，用于排序扫描结果。可选参数，可以传递 NULL 来按默认顺序返回扫描结果。
alphasort 是一个比较函数，用于排序文件和子目录的名称
返回值：

成功：返回找到的文件和子目录的数量。
失败：返回 -1，并设置 errno。*/
	/* 扫描目录,结果按名字排序,存在aptNameList[0],aptNameList[1],... */
	iNumber = scandir(strDirName, &aptNameList, 0, alphasort);
	if (iNumber < 0)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"scandir error : %s!\n", strDirName);
		return -1;
	}

	/* 忽略".", ".."这两个目录，故减去2 */
	aptDirContents = malloc(sizeof(PT_DirContent) * (iNumber - 2));
	if (NULL == aptDirContents)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"malloc error!\n");
		return -1;
	}
    *pptDirContents = aptDirContents;
	/*分配内存，存储结构体*/
	for (i = 0; i < iNumber - 2; i++)
	{
		aptDirContents[i] = malloc(sizeof(T_DirContent));
		if (NULL == aptDirContents)
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"malloc error!\n");
			return -1;
		}
	}
	/* 先把目录挑出来存入aptDirContents */
	for (i = 0, j = 0; i < iNumber; i++)
	{
		/* 忽略".",".."这两个目录 */
		if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
			continue;
         /* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type */
		/* if (aptNameList[i]->d_type == DT_DIR) */
        if (isDir(strDirName, aptNameList[i]->d_name))
		{
			strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
			aptDirContents[j]->strName[255] = '\0';
			aptDirContents[j]->eFileType    = FILETYPE_DIR;
            DBG_PRINTF(DLOG_LVL_DEBUG,DLOG_TAG,"%s %d\n",aptDirContents[j]->strName,aptDirContents[j]->eFileType);
            free(aptNameList[i]);
            aptNameList[i] = NULL;
			j++;
		}
	}
	/* 再把常规文件挑出来存入aptDirContents */	
	for (i = 0; i < iNumber; i++)
	{
        if (aptNameList[i] == NULL)
            continue;
		if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
			continue;
        
        if (isRegFile(strDirName, aptNameList[i]->d_name))
		{
			strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
			aptDirContents[j]->strName[255] = '\0';
            pcRet = strchr(aptDirContents[j]->strName,'.');
            if(pcRet == NULL)
            {
                aptDirContents[j]->eFileType    = FILETYPE_FILE_DEFAULT;
            }
            else if(strncmp(pcRet+1,"bmp",3) == 0)
            {
			    aptDirContents[j]->eFileType    = FILETYPE_FILE_BMP;
            }
            else if(strncmp(pcRet+1,"txt",3) == 0)
            {
                aptDirContents[j]->eFileType    = FILETYPE_FILE_TXT;
            }
            else if(strncmp(pcRet+1,"jpg",3) == 0)
            {
                aptDirContents[j]->eFileType    = FILETYPE_FILE_JPEG;
            }
            else if(strncmp(pcRet+1,"png",3) == 0)
            {
                aptDirContents[j]->eFileType    = FILETYPE_FILE_PNG;
            }
            else
            {
                aptDirContents[j]->eFileType    = FILETYPE_FILE_DEFAULT;
            }
            DBG_PRINTF(DLOG_LVL_DEBUG,DLOG_TAG,"%s %d\n",aptDirContents[j]->strName,aptDirContents[j]->eFileType);
            free(aptNameList[i]);
            aptNameList[i] = NULL;
			j++;
		}
        
	}
	/* 释放aptDirContents中未使用的项 */
	for (i = j; i < iNumber - 2; i++)
	{
		free(aptDirContents[i]);
	}

	/* 释放scandir函数分配的内存 */
	for (i = 0; i < iNumber; i++)
	{
        if (aptNameList[i])
        {
    		free(aptNameList[i]);
        }
	}
	free(aptNameList);

	*piNumber = j;
	return 0;
}
/*
 * @description		    	: 释放目录下文件所占内存
 * @param - aptDirContents	: 文件描述结构体指针
 * @param - iNumber		 	: 文件总数
 * @return 			    	: 1 是;其他 不是
 */
void FreeDirContents(PT_DirContent *aptDirContents, int iNumber)
{
	int i;
	for (i = 0; i < iNumber; i++)
	{
		free(aptDirContents[i]);
	}
	free(aptDirContents);
}
